#include <iostream>
#include <limits>

#include "point3d.h"
#include "vec3.h"

#define INF std::numeric_limits<double>::max()
#define EPS std::numeric_limits<double>::epsilon()

#include "curvendimension.hpp"

typedef CurvePoligonal< real , 3 , Point3D , Vec3 > Curve;

class testCurve : public Curve
{
public:
    testCurve( void ): Curve(){}
    testCurve (const std::vector<Point3D>& points) : Curve(points){}
    testCurve (const testCurve& c) : Curve(c){}
    testCurve (const Curve& c) : Curve(c){}

    void print( const char* name ){
        for( uint32 i = 0 ; i < size() ; i++ ) std::cout <<  name << "[" << i << "] =( " << this->at(i)[0] << ", " <<   this->at(i)[1] << ", " <<   this->at(i)[2] << ") " << std::endl;
        if(isClosed()) std::cout <<  name << "[" << 0 << "] =( " << this->at(0)[0] << ", " <<   this->at(0)[1] << ", " <<   this->at(0)[2] << ") " << std::endl;
        std::cout << std::flush ;
    }
};

int main(void)
{
    std::vector<Point3D> pT;
    for( int i = -5 ; i < 5 ; ++i ) pT.push_back( Point3D((real)i , (real)i*i , std::log( (real)i*i + 1 )) ) ;
    testCurve cT, dT(pT);

    cT = testCurve(dT) ;
    pT = cT.getPoints();
    for( uint32 i = 0 ; i < pT.size() ; ++i ) std::cout << "pT.at(" << i << ") =( " << pT.at(i)[0] << ", " << pT.at(i)[1] << ", " << pT.at(i)[2] << ") " << std::endl;

    dT.setPoints(pT);

    std::cout << "dT[7] ( " << dT[7][0] << ", " << dT[7][1] << ", "
              << dT[7][2] << ")  --  dT.size()  =  " << dT.size() << std::endl;
    dT.clear();
    std::cout << "dT.clear() -- dT.size()  =  " << dT.size() << std::endl;
    std::cout << "cT(0.5) =( " << cT.eval(.5)[0] << ", " << cT.eval(.5)[1] << ", " << cT.eval(.5)[2] << ") " << std::endl;
    std::cout << " cT is Closed? " << ( cT.isClosed() ? " yes " : " no " ) << std::endl;
    cT.close();
    std::cout << " cT is Closed? " << ( cT.isClosed() ? " yes " : " no " )<< std::endl;
    cT.open();
    std::cout << " cT is Closed? " << ( cT.isClosed() ? " yes " : " no " ) << std::endl;
    std::cout << "--------" << std::endl;
    dT.copy(cT);

    cT.setPoint( 4 , Point3D(-1,1,2));
    cT.insert(4,Point3D(-1.0,.9,1.9));
    cT.add( Point3D(11,21,13) );
    cT[6] = Point3D(0,0,1);
    std::cout << "cT.at(i)" << std::endl;
    for( uint32 i = 0 ; i < cT.size() ; ++i ) std::cout << "cT.at(" << i << ") =( " << cT.at(i)[0] << ", " << cT.at(i)[1] << ", " << cT.at(i)[2] << ") " << std::endl;
    std::cout << "cT.at(i)" << std::endl;
    std::cout << "cT.atBegin() =( " << cT.atBegin()[0] << ", " << cT.atBegin()[1] << ", " << cT.atBegin()[2] << ") " << std::endl;
    cT.push_back( Point3D(23,32,33));
    std::cout << "cT.atEnd() =( " << cT.atEnd()[0] << ", " << cT.atEnd()[1] << ", " << cT.atEnd()[2] << ") " << std::endl;
    cT.pop_back();
    std::cout << "cT.atEnd() =( " << cT.atEnd()[0] << ", " << cT.atEnd()[1] << ", " << cT.atEnd()[2] << ") " << std::endl;

    Point3D p;
    uint32 index = cT.eval(0.5,p);
    std::cout << "index = cT.at(0.5,p) = " << index << " p =( " << p[0] << ", " << p[1] << ", " << p[2] << ") " << std::endl;
    std::cout << "--------" << std::endl;
    std::cout << "cT.chaikinSubDivide(2)" << std::endl;
    cT.chaikinSubDivide( 2 );
    cT.print("cT");
    std::cout << "cT.chaikinSubDivide(2)" << std::endl;

    std::cout << "cT.chaikinFilter(2)" << std::endl;
    cT.chaikinFilter( 2 );
    cT.print("cT");
    std::cout << "cT.chaikinFilter(2)" << std::endl;

    std::cout << "cT.superSample(.4)" << std::endl;
    cT.superSample( .4 );
    for( uint32 i = 0 ; i < cT.size() ; i = i+10 ) std::cout << "cT.at(" << i << ") =( " << cT[i][0] << ", " << cT[i][1] << ", " << cT[i][2] << ") " << std::endl;
    std::cout << "cT.superSample(.4)" << std::endl;

    std::cout << "cT.lineFilter( .6 )" << std::endl;
    cT.chaikinFilter( 3 );
    cT.lineFilter( .6 );
    cT.print("cT");
    std::cout << "cT.lineFilter( .6 )" << std::endl;

    std::cout << "cT.meanFilter( )" << std::endl;
    cT.meanFilter();
    cT.print("cT");
    std::cout << "cT.meanFilter( .6 )" << std::endl;
    
    cT.close();
    std::cout << "cT.meanFilter(  ) -- Close " << std::endl;
    cT.meanFilter();
    cT.print("cT");
    std::cout << "cT.meanFilter(   ) -- Close " << std::endl;
    cT.open();
    
    cT.reverse();
    std::cout << "cT.reverse()" << std::endl;
    cT.print("cT");
    std::cout << "cT.reverse()" << std::endl;

    cT.join(dT);
    std::cout << "cT.join(dT)" << std::endl;
    cT.print("cT");
    std::cout << "cT.join(dT)" << std::endl;

    std::cout << "dT.tangentEval( u )" << std::endl;
    for( uint32 i = 0 ; i < dT.size()*2 ; i++ ) std::cout << "dT.tangentEval(" << real(i)/((dT.size()*2)-1) << ") =( " << dT.tangentEval( real(i)/((dT.size()*2)-1))[0] << ", " << dT.tangentEval( real(i)/((dT.size()*2)-1))[1] << ", " << dT.tangentEval( real(i)/((dT.size()*2)-1))[2] << ") " << std::endl;
    std::cout << "dT.tangentEval( u )" << std::endl;

    std::cout << "dT.tangent(i)" << std::endl;
    for( uint32 i = 0 ; i < dT.size() ; i++ ) std::cout << "dT.tangent(" << i << ") =( " <<dT.tangent(i)[0] << ", " << dT.tangent(i)[1] << ", " << dT.tangent(i)[2] << ") " << std::endl;
    std::cout << "dT.tangent(i)" << std::endl;

    std::cout << "dT.tangentEvalContinuos( u )" << std::endl;
    for( uint32 i = 0 ; i < dT.size()*2 ; i++ ) std::cout << "dT.tangentEvalContinuos(" << real(i)/((dT.size()*2)-1) << ") =( " << dT.tangentEvalContinuos( real(i)/((dT.size()*2)-1))[0] << ", " << dT.tangentEvalContinuos( real(i)/((dT.size()*2)-1))[1] << ", " << dT.tangentEvalContinuos( real(i)/((dT.size()*2)-1))[2] << ") " << std::endl;
    std::cout << "dT.tangentEvalContinuos( u )" << std::endl;

    Point3D min, max;
    dT.minMax( min,max );
    std::cout << "---------------\nmin = ( " << min[0] << " , " << min[1] << " , " << min[2] << " ) max =  " << max[0] << " , "   << max[1] << " , "   << max[2] << " ) " << std::endl;

    dT.translate( Vec3(1,1,1));
    std::cout << "dT.translate( 1,1,1 )" << std::endl;
    dT.print("dT");
    std::cout << "dT.translate( 1,1,1 )" << std::endl;

    real u = 0.0 ;
    real dist = dT.projectPoint(Point3D(2,2,2),index,u,p);
    std::cout << "---------------\ndT.projectPoint(Point3D(2,2,2),index,p); p = ( "
              << p[0] << " , " << p[1] << " , " << p[2] << " ) dist =  "
              << dist << " ;  index =  "   << index
              << "  -- dT.eval(" << u << ") =( "
              << dT.eval(u)[0] << ", " << dT.eval(u)[1] << ", " << dT.eval(u)[2] << ") "<< std::endl;
    dist = dT.projectPoint(Point3D(200,200,200),index,u,p);
    std::cout << "---------------\ndT.projectPoint(Point3D(200,200,200),index,p); p = ( "
              << p[0] << " , " << p[1] << " , " << p[2] << " ) dist =  "
              << dist << " ;  index =  "   << index
              << "  -- dT.eval(" << u << ") =( "
              << dT.eval(u)[0] << ", " << dT.eval(u)[1] << ", " << dT.eval(u)[2] << ") "<< std::endl;
    dist = dT.projectPoint(Point3D(-4, 26, 4.2581),index,u,p);
    std::cout << "---------------\ndT.projectPoint(Point3D(-4, 26, 4.2581),index,p); p = ( "
              << p[0] << " , " << p[1] << " , " << p[2] << " ) dist =  "
              << dist << " ;  index =  "   << index
              << "  -- dT.eval(" << u << ") =( "	
              << dT.eval(u)[0] << ", " << dT.eval(u)[1] << ", " << dT.eval(u)[2] << ") "<< std::endl;
    dist = dT.projectPoint(Point3D(2.75078, 4.75078, 2.36022),index,u,p);
    std::cout << "---------------\ndT.projectPoint(Point3D(2.75078, 4.75078, 2.36022),index,p); p = ( "
              << p[0] << " , " << p[1] << " , " << p[2] << " ) dist =  "
              << dist << " ;  index =  "   << index
              << "  -- dT.eval(" << u << ") =( "
              << dT.eval(u)[0] << ", " << dT.eval(u)[1] << ", " << dT.eval(u)[2] << ") "<< std::endl;
    std::cout << "---------------\n";
    std::cout << "dT.distanceTo( Point3D(200,200,200) ) = " << dT.distanceTo( Point3D(200,200,200) ) << std::endl;
    std::cout << "---------------\n";

    dT.scale(Vec3(2,.5,5));
    std::cout << "dT.scale(Vec3(2,.5,5));" << std::endl;
    dT.print("dT");
    std::cout << "dT.scale(Vec3(2,.5,5));" << std::endl;

    dT.clear();
    cT.clear();

    std::cout << "-------------\ndT :: Circle" << std::endl;
    for( uint32 i = 0 ; i < 8 ; ++i ) dT.add( Point3D( std::cos(i*0.25*M_PI) , std::sin(i*0.25*M_PI) , std::cos(i*0.25*M_PI)*std::cos(i*0.25*M_PI) ) );
    dT.print("dT");

    dT.close();

    p = dT.centroid();
    std::cout << "---------------\np = dT.centroid() = ( "
              << p[0] << " , " << p[1] << " , " << p[2] << " ) " << std::endl;

    testCurve eT = dT , fT;

    cT = dT.split( 5 );
    std::cout << "\n--------------- cT = dT.split( 5 ) -- close --\n" ;
    dT.print("dT");
    std::cout << "---------------\n" ;
    cT.print("cT");
    std::cout << "---------------\n" ;

    dT = cT.split( 3 );
    std::cout << "\n--------------- dT = cT.split( 3 ) -- open --\n" ;
    cT.print("cT");
    std::cout << "---------------\n" ;
    dT.print("dT");
    std::cout << "---------------\n" ;

    bool b = eT.split( 3 , cT , dT );
    std::cout << "\n--------------- eT.split( 3 , cT , dT )  -- close --  " << (b? "split" : "not split") << std::endl;
    cT.print("cT");
    std::cout << "---------------\n" ;
    dT.print("dT");
    std::cout << "---------------\n" ;
    eT.print("eT");
    std::cout << "---------------\n" ;
    std::cout << EPS << std::endl;

    b = eT.split( Point3D(-.7,.7,.5) , cT , dT , false );
    std::cout << "\n-------- b = eT.split( Point3D(-.7,.7,.5) , cT , dT , false );  -- close --  " << (b? "split" : "not split") << std::endl;
    cT.print("cT");
    std::cout << "---------------\n" ;
    dT.print("dT");
    std::cout << "---------------\n" ;

    b = eT.split( Point3D(-.7,.7,.5) , cT , dT , false , 1.0 );
    std::cout << "\n-------- b = eT.split( Point3D(-.7,.7,.5) , cT , dT , false , 1.0 );  -- close --  " << (b? "split" : "not split") << std::endl;
    cT.print("cT");
    std::cout << "---------------\n" ;
    dT.print("dT");
    std::cout << "---------------\n" ;
    eT.print("eT");
    std::cout << "---------------\n" ;

    b = eT.split( Point3D( 1.0, -.1,.75) , cT , dT , true , 1.0 );
    std::cout << "\n-------- b = eT.split( Point3D( 1.0, -.1,.75) , cT , dT , true , 1.0 );  -- close --  " << (b? "split" : "not split") << std::endl;
    cT.print("cT");
    std::cout << "---------------\n" ;
    dT.print("dT");
    std::cout << "---------------\n" ;

    b = eT.split( Point3D( 1.0, -.1,.75) , cT , dT );
    std::cout << "\n-------- b = eT.split( Point3D( 1.0, -.1,.75) , cT , dT );  -- close --  " << (b? "split" : "not split") << std::endl;
    cT.print("cT");
    std::cout << "---------------\n" ;
    dT.print("dT");
    std::cout << "---------------\n" ;

    b = eT.split( 2 , 5  , cT , dT , fT );
    std::cout << "\n-------- b = eT.split( 2 , 3  , cT , dT ,fT );  -- close --  " << (b? "split" : "not split") << std::endl;
    cT.print("cT");
    std::cout << "---------------\n" ;
    dT.print("dT");
    std::cout << "---------------\n" ;
    fT.print("fT");
    std::cout << "---------------\n" ;

    eT.open();
    b = eT.split( 2 , 5  , cT , dT , fT );
    std::cout << "\n-------- b = eT.split( 2 , 3  , cT , dT ,fT );  -- open --  "  << std::endl;
    cT.print("cT");
    std::cout << "---------------\n" ;
    dT.print("dT");
    std::cout << "---------------\n" ;
    fT.print("fT");
    std::cout << "---------------\n" ;
    eT.print("eT");
    std::cout << "---------------\n" ;
    fT.clear();
    std::cout << "------------- fT :: half-circle center (0.6 , 0.6)" << std::endl;
    for( uint32 i = 0 ; i < 4 ; ++i ) fT.add( Point3D( std::cos(i*0.25*M_PI) + 0.6 , std::sin(i*0.25*M_PI) + 0.6 , std::cos(i*0.25*M_PI)*std::cos(i*0.25*M_PI) ) );
    fT.print("fT");
    std::cout << "------------- fT :: half-circle center (0.6 , 0.6) END" << std::endl;

    dT = fT.overSketch( eT , cT );
    std::cout << "\n-------- dT = fT.overSketch( eT , cT ); " << (eT.isClosed() ? "-- Closed --" :  "-- Open --" ) << std::endl;
    eT.print("eT");
    std::cout << "---------------"  << (fT.isClosed() ? "-- Closed --" :  "-- Open --" ) << std::endl;
    fT.print("fT");
    std::cout << "---------------"  << (dT.isClosed() ? "-- Closed --" :  "-- Open --" ) << std::endl;
    dT.print("dT");
    std::cout << "---------------"  << (cT.isClosed() ? "-- Closed --" :  "-- Open --" ) << std::endl;
    eT.print("cT");
    std::cout << "---------------"  << std::endl;

    std::cout << EPS << std::endl;
    std::cout <<  std::flush;
}
